#include "pref.hpp"

void PrefDef::define (string &_cliName, string &_confName, bool &_stackable, 
		      Type &_type, string &_defaultVal, string &_description)
{
  this->cliName     = _cliName;
  this->confName    = _confName;
  this->stackable   = _stackable;
  this->type        = _type;
  this->defaultVal  = _defaultVal;
  this->description = _description;

  assert (type == PT_STR || type == PT_INT || type == PT_BOOL);
  return ;
}

string PrefDef::getValue (unsigned int &idx)
{
  PrefVal * val = valList.lookup (&idx, sizeof (unsigned int));
  if (val == NULL)
    return "";
  else
    return val->getVal ();
}

bool PrefDef::setValue (string &value)
{
  switch (this->type)
    {
    case PT_BOOL:
      if (value != "true"  && value != "t" && value != "yes" && 
	  value != "false" && value != "f" && value != "no")
	return false;
      break;

    case PT_INT:
      if (string::npos != value.find_first_not_of ("0123456789"))
	return false;
      break;

    default:
      break;
    }

  PrefVal * val = valList.fetch (&(this->idx), sizeof (this->idx));  
  this->idx++;
  val->setVal (value);
  return true;
}



bool Preference::define (string cliName, string confName, bool stackable, PrefDef::Type type, 
			 string defaultVal, string description)
{
  PrefDef * def;
  if (NULL != this->defList.lookup ((void*)(cliName.c_str ()), cliName.length ()))
    {
      this->errmsg = "Duplicated option name '" + cliName + "'";
      return false; // existing definition
    }

  def = this->defList.fetch ((void*)(cliName.c_str ()), cliName.length ());
  def->define (cliName, confName, stackable, type, defaultVal, description);
  return true;
}

bool Preference::import (int c, char *v[])
{
  PrefDef * def = NULL, * valDef = NULL;

  try
    {
      for (int i = 1; i < c; i++)
	{
	  if (valDef)
	    {
	      string value = v[i];
	      if (! valDef->setValue (value))
		throw "Invalid option value '" + value + "' (Invalid format)";

	      valDef = NULL;
	    }
	  else
	    {	  
	      char * p = v[i];
	      string value = v[i];
	      string valTrue = "true";

	      if (strlen (p) < 2 || *p != '-')
		throw "Invalid argument synta '" + value + "'";

	      if (*(p + 1) == '-') // multi letter option
		{
		  if (NULL == (def = this->defList.lookup ((void*)(p + 2), strlen (p + 2))))
		    throw "Invalid option name '" + value + "'";

		  if (def->getType () != PrefDef::PT_BOOL)
		    valDef = def;
		  else
		    assert (def->setValue (valTrue));
		}
	      else // single letter option
		{
		  for (p = p + 1; *p != '\0'; p++)
		    {
		      if (NULL == (def = this->defList.lookup (p, 1)))
			throw "Invalid option name '" + value.substr (p - v[i], 1) + "'";

		      if (def->getType () != PrefDef::PT_BOOL)
			{
			  if (valDef)
			    throw "Invalid option set '" + value + "'";
			  else
			    valDef = def;
			}
		      else
			assert (def->setValue (valTrue));		  
		    }
		}
	    }
	}

      if (valDef)
	throw "'" + valDef->getCliName () + "' option need user value";
    }
  catch (string e)
    {
      this->errmsg += e;
      return false;
    }
  
  return true;
}

int Preference::lookup (string name)
{
  PrefDef * def = this->defList.lookup ((void*)name.c_str (), name.length ());
  if (def == NULL)
    return -1;

  return def->getCount ();
}

string Preference::getStr (string name, unsigned int idx)
{
  PrefDef * def = this->defList.lookup ((void*)name.c_str (), name.length ());
  assert (def);   // this function can be called also when type is not STR

  if (def->getCount () == 0)
    return def->getDefault ();
  if (def->getCount () <= idx)
    return "";

  return def->getValue (idx);
}


int Preference::getInt (string name, unsigned int idx)
{
  PrefDef * def = this->defList.lookup ((void*)name.c_str (), name.length ());
  assert (def && def->getType () == PrefDef::PT_INT);

  string val;
  if (def->getCount () == 0)
    val = def->getDefault ();
  else if (def->getCount () <= idx)
    return 0;
  else
    val = def->getValue (idx);

  char *e;
  int i = strtol (val.c_str (), &e, 0);
  assert (*e == '\0');
    
  return i;
}

bool Preference::getBool (string name, unsigned int idx)
{
  PrefDef * def = this->defList.lookup ((void*)name.c_str (), name.length ());
  assert (def && def->getType () == PrefDef::PT_BOOL);

  if (def->getCount () <= idx)
    return false;

  string val;
  if (def->getCount () == 0)
    val = def->getDefault ();
  else
    val = def->getValue (idx);
  if (val == "true" || val == "t" || val == "yes")
    return true;
  else if (val == "false" || val == "f" || val == "no")
    return false;
  else 
    assert (0);
}  
